﻿/*************************************************************************************
 * 工    具：  HK_SmartCopy
 * 
 * 描    述：  HK_SmartCopy 智能复制工具，借鉴 Maya 的智能复制功能，能在 Unity 中实现高效的物体对象的智能复制。该工具通过精准记录位置、旋转和缩放的偏移量，
 *            助力开发者快速创建连续且增量的复制对象，极大提升场景中物体排列与布局的工作效率。具体使用方式如下：
 *            初始复制：按 Ctrl+Shift+D 触发智能复制操作，操作前需确保在 Hierarchy 面板或 Scene 场景中仅选中一个物体对象。
 *            设置偏移：对首次复制出的对象进行位移、旋转或缩放调整，工具会自动记录这些调整产生的偏移量。
 *            连续复制：再次按下 Ctrl+Shift+D 工具将依据记录的偏移量持续创建新的复制对象。
 *            自动重置：若当前选中对象发生变化，工具会自动重置复制流程，重新开始记录偏移量。
 * 
 * 版    本：  V1.2
 * 作    者：  京产肠饭
 * 
 * 创    建：  2021/08/26  V1.0
 * 
 * 更    新：  2024/10/06  V1.1
 *            1.功能优化：优化激活方式，移除代码中对 HK_GlobalSetting 的依赖。

 *            2025/06/21  V1.2
 *            1.代码优化：使用 Vector3.Distance 比较位置和缩放，用 Quaternion.Angle 比较旋转，避免浮点数精度问题。
 *            2.代码优化：使用 Quaternion 进行旋转操作，避免万向锁问题。
 * 
 * 链    接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using UnityEditor;
using UnityEngine;

namespace HKTools
{
    [InitializeOnLoad]
    public class HK_SmartCopy
    {
        public static void ChangeState(bool isActive)
        {
            IsActive = isActive;
        }

        // 是否激活
        static bool IsActive;

        // 执行的步骤
        static int moveStep = 0;

        // 选中的第一个物体 初始位置、旋转、缩放
        static Vector3 startPos, startScale;
        static Quaternion startRotation; // 添加四元数记录初始旋转
        static Vector3 firstForward, firstRight, firstUp;

        //通过复制出来的第一个物体 计算出来的 偏移位置、旋转、缩放
        static Vector3 offsetPos, offsetX, offsetY, offsetZ;
        static float offsetXVal, offsetYVal, offsetZVal;
        static Quaternion offsetRotation; // 添加四元数记录旋转偏移
        static float xScaleIndex, yScaleIndex, zScaleIndex;

        //记录上一个复制的 位置和旋转
        static Vector3 lastPos;
        static Quaternion lastRotation;
        static Vector3 lastScale;

        static GameObject curSelectGo;

        // 验证方法，设置该工具是否启用
        [MenuItem("HKTools/Smart Copy (智能复制) %#D", true, HK_MenuPriority.HK_SmartCopy)]
        static bool CopyObjValidate()
        {
            return IsActive;
        }

        [MenuItem("HKTools/Smart Copy (智能复制) %#D", priority = HK_MenuPriority.HK_SmartCopy)]
        static void CopyObj()
        {
            // 判断是否只选中了一个物体 
            if (Selection.gameObjects.Length > 1)
            {
                Debug.LogError("HK_SmartCopy：只能选择一个Hierarchy中的物体进行复制!");
                return;
            }
            if (Selection.gameObjects.Length == 0)
            {
                Debug.LogError("HK_SmartCopy：未选中物体，无法执行复制操作!");
                return;
            }

            switch (moveStep)
            {
                case 0:
                    // 第一步：记录是否有 Parent，Copy选中物体，记录第一个物体 三个轴向 和 初始位置旋转
                    InitialStep();
                    break;
                case 1:
                    // 第二步 ： 判定 偏移向量 在各个轴向的分量，并复制
                    RecordOffset();
                    break;
                case 2:
                    // 第三步：就是一直复制呗
                    IncrementalCopy();
                    break;
            }
        }

        static void InitialStep()
        {
            EditorApplication.ExecuteMenuItem("Edit/Duplicate");

            curSelectGo = Selection.activeGameObject;

            //记录第一个物体的 三个轴向 & 开始的 位置和旋转
            firstForward = curSelectGo.transform.forward;
            firstRight = curSelectGo.transform.right;
            firstUp = curSelectGo.transform.up;

            startPos = curSelectGo.transform.position;
            // 修改为记录四元数
            startRotation = curSelectGo.transform.rotation;
            startScale = curSelectGo.transform.localScale;

            moveStep++;
        }

        static void RecordOffset()
        {
            // 如果换了选中物体，或者选中物体位置与旋转没发生改变，则返回第一步
            if (Selection.activeGameObject != curSelectGo ||
                (Vector3.Distance(Selection.activeGameObject.transform.position, startPos) < 0.001f &&
                 Quaternion.Angle(Selection.activeGameObject.transform.rotation, startRotation) < 0.001f &&
                 Vector3.Distance(Selection.activeGameObject.transform.localScale, startScale) < 0.001f))
            {
                moveStep = 0;
                CopyObj();
                return;
            }

            //计算偏移的向量 和 每个轴向的分量
            offsetPos = Selection.activeGameObject.transform.position - startPos;

            offsetX = Vector3.Project(offsetPos, firstRight);
            offsetY = Vector3.Project(offsetPos, firstUp);
            offsetZ = Vector3.Project(offsetPos, firstForward);

            if (Vector3.Dot(firstRight.normalized, offsetX.normalized) > 0)
                offsetXVal = offsetX.magnitude;
            else
                offsetXVal = -offsetX.magnitude;
            if (Vector3.Dot(firstForward.normalized, offsetZ.normalized) < 0)
                offsetZVal = -offsetZ.magnitude;
            else
                offsetZVal = offsetZ.magnitude;
            if (Vector3.Dot(firstUp.normalized, offsetY.normalized) < 0)
                offsetYVal = -offsetY.magnitude;
            else
                offsetYVal = offsetY.magnitude;

            // 计算旋转偏移，使用四元数
            offsetRotation = Quaternion.Inverse(startRotation) * Selection.activeGameObject.transform.rotation;

            EditorApplication.ExecuteMenuItem("Edit/Duplicate");

            Selection.activeGameObject.transform.Translate(offsetXVal, offsetYVal, offsetZVal, Space.Self);
            // 使用四元数进行旋转
            Selection.activeGameObject.transform.rotation *= offsetRotation;

            lastPos = Selection.activeGameObject.transform.position;
            lastRotation = Selection.activeGameObject.transform.rotation;

            xScaleIndex = Selection.activeGameObject.transform.localScale.x / startScale.x;
            yScaleIndex = Selection.activeGameObject.transform.localScale.y / startScale.y;
            zScaleIndex = Selection.activeGameObject.transform.localScale.z / startScale.z;

            float x = Selection.activeGameObject.transform.localScale.x * xScaleIndex;
            float y = Selection.activeGameObject.transform.localScale.y * yScaleIndex;
            float z = Selection.activeGameObject.transform.localScale.z * zScaleIndex;

            Selection.activeGameObject.transform.localScale = new Vector3(x, y, z);

            lastScale = Selection.activeGameObject.transform.localScale;

            curSelectGo = Selection.activeGameObject;

            moveStep++;
        }

        static void IncrementalCopy()
        {
            if (Selection.activeGameObject != curSelectGo)
            {
                moveStep = 0;
                CopyObj();
                return;
            }

            if (Vector3.Distance(Selection.activeGameObject.transform.position, lastPos) > 0.01f
                || Quaternion.Angle(Selection.activeGameObject.transform.rotation, lastRotation) > 0.1f
                || Vector3.Distance(Selection.activeGameObject.transform.localScale, lastScale) > 0.01f)
            {
                Debug.Log("重新复制");
                moveStep = 0;
                CopyObj();
                return;
            }

            EditorApplication.ExecuteMenuItem("Edit/Duplicate");

            Selection.activeGameObject.transform.Translate(offsetXVal, offsetYVal, offsetZVal, Space.Self);
            // 使用四元数进行旋转
            Selection.activeGameObject.transform.rotation *= offsetRotation;

            lastPos = Selection.activeGameObject.transform.position;
            lastRotation = Selection.activeGameObject.transform.rotation;

            float x = Selection.activeGameObject.transform.localScale.x * xScaleIndex;
            float y = Selection.activeGameObject.transform.localScale.y * yScaleIndex;
            float z = Selection.activeGameObject.transform.localScale.z * zScaleIndex;

            Selection.activeGameObject.transform.localScale = new Vector3(x, y, z);

            lastScale = Selection.activeGameObject.transform.localScale;

            curSelectGo = Selection.activeGameObject;
        }
    }
}